package controllers

import java.io.File
import java.nio.file.Files

import config.{Blast, Global, MyFile}
import javax.inject.Inject
import org.apache.commons.io.FileUtils
import play.api.data.Form
import play.api.data.Forms._
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
import utils.ExecCommand

import scala.concurrent.ExecutionContext
import scala.jdk.CollectionConverters._

/**
 * I:\camellia_oleifera_abel_db\tools\ncbi-blast-2.6.0+\bin\makeblastdb.exe -dbtype nucl -in ./genome.fa -parse_seqids -out genome -max_file_sz 2000000000
 * @param cc
 * @param exec
 */
class BlastController @Inject()(cc: ControllerComponents)
                               (implicit exec: ExecutionContext) extends AbstractController(cc) with MyFile with Blast {


  def blastPage = Action { implicit request =>
    Ok(views.html.blast.blastPage())
  }

  case class blastData(blastType: String, method: String, queryText: String, db: Seq[String], evalue: String, wordSize: String, maxTargetSeqs: String)

  val blastForm = Form(
    mapping(
      "blastType" -> text,
      "method" -> text,
      "queryText" -> text,
      "db" -> seq(text),
      "evalue" -> text,
      "wordSize" -> text,
      "maxTargetSeqs" -> text
    )(blastData.apply)(blastData.unapply)
  )

  def blastRun = Action(parse.multipartFormData) { implicit request =>
    val data = blastForm.bindFromRequest.get
    val tmpDir = Files.createTempDirectory("tmpDir").toString
    val seqFile = new File(tmpDir, "seq.fa")
    data.method match {
      case "text" =>
        FileUtils.writeStringToFile(seqFile, data.queryText)
      case "file" =>
        val file = request.body.file("file").get
        file.ref.moveTo(seqFile, replace = true)
    }

    val outXml = new File(tmpDir, "out.xml")
    val outHtml = new File(tmpDir, "out.html")
    val outTable = new File(tmpDir, "table.xls")

    val blastType = data.blastType match {
      case "blastnGenome" => "blastn"
      case x: String => x
    }

    val db = data.blastType match {
      case "blastn" => s"${Global.path}/blastData/cds/cds"
      case "blastnGenome" => s"${Global.path}/blastData/genome/genome"
      case "blastp" => s"${Global.path}/blastData/pep/pep"
    }

    val seqlist = new File(s"$tmpDir/seqid.txt")

    data.blastType match {
      case x if x == "blastn" || x == "blastp" =>
        data.db.foreach { x =>
          println(x)
          val geneid = s"${Global.path}/blastData/${x}_geneId.txt".toFile.readLines
          FileUtils.writeLines(seqlist, geneid.asJava, true)
        }
      case "blastnGenome" =>
        data.db.foreach { x =>
          val chr = s"${Global.path}/blastData/${x}_chr.txt".toFile.readLines
          FileUtils.writeLines(seqlist, chr.asJava, true)
        }
    }



    val command1 = Global.path + "/tools/ncbi-blast-2.6.0+/bin/%s%s -query ".format(blastType, Global.suffix) + seqFile.getAbsolutePath + " -db " +
      db + " -seqidlist " + seqlist.getAbsolutePath + " -outfmt 5 -evalue " + data.evalue + " -max_target_seqs " + data.maxTargetSeqs +
      " -word_size " + data.wordSize + " -out " + outXml.getAbsolutePath

    val jinja = blastType
    val command2 = "python " + Global.path + s"/tools/blast2html/blast2html.py -i " + outXml.getAbsolutePath + " -o " +
      outHtml.getAbsolutePath +
      " --template %s/tools/blast2html/%s.jinja".format(Global.path, jinja)
    val command3 = "perl " + Global.path + "/tools/Blast2table -format 10 -xml " + outXml.getAbsolutePath + " -header "

    val execCommand = new ExecCommand
    execCommand.exect(Array(command1, command2, command3), tmpDir)

    if (execCommand.isSuccess) {

      val excel = execCommand.getOutStr
      FileUtils.writeStringToFile(outTable, excel)
      Ok(Json.obj("html" -> tmpDir.replaceAll("\\\\", "/"),
        "excel" -> excel, "types" -> data.blastType))
    } else {
      tmpDir.delete
      Ok(Json.obj("valid" -> "false", "message" -> execCommand.getErrStr))
    }
  }

  def blastResultBefore(path: String, types: String): Action[AnyContent] = Action {
    implicit request =>
      Ok(views.html.blast.blastResult(path, types))
  }

  def blastResult(path: String, types: String): Action[AnyContent] = Action {
    implicit request =>
      val html = (path + "/out.html").readFileToString
      val json = if (types == "blastnGenome") {
        val block = path.getSyntenyData
        Json.obj("html" -> (html + "\n" + scriptHtml), "block" -> block)
      } else {
        Json.obj("html" -> (html + "\n" + scriptHtml))
      }
      path.delete
      Ok(json)
  }


  def downloadBlastByRange(name: String, range: String, types: String) = Action {
    val fasta = types match {
      case "blastnGenome" => s"${Global.path}/blastData/genome/genome.fa"
      case "blastn" => s"${Global.path}/blastData/cds/cds.fa"
      case "blastp" => s"${Global.path}/blastData/pep/pep.fa"
    }
    val execCommand = new ExecCommand
    range.split("Range").tail.foreach { x =>
      val r = x.trim.split(":").last.split("to").map(_.trim).sortBy(_.toInt)
      val ra = name + ":" + r.mkString("-")
      val command = s"${Global.samtools} faidx $fasta $ra"
      execCommand.exec(command)
    }

    val seq = execCommand.getOutStr
    Ok(seq).withHeaders(
      //缓存
      CACHE_CONTROL -> "max-age=3600",
      CONTENT_DISPOSITION -> ("attachment; filename=" + name + "_aligned.fasta"),
      "HttpResponse.entity.contentType" -> "application/x-download"
    )
  }

  def downloadBlastByName(name: String, types: String) = Action {
    val fasta = types match {
      case "blastnGenome" => s"${Global.path}/blastData/genome/genome.fa"
      case "blastn" => s"${Global.path}/blastData/cds/cds.fa"
      case "blastp" => s"${Global.path}/blastData/pep/pep.fa"
    }
    val execCommand = new ExecCommand
    val command = s"${Global.samtools} faidx $fasta $name"

    val tmpDir = Files.createTempDirectory("tmpDir").toString
    val seqFile = new File(tmpDir + "/seq.fasta")
    execCommand.execo(command, seqFile)
    println(execCommand.getErrStr)
    Ok.sendFile(seqFile, onClose = () => {
      tmpDir.delete
    }).withHeaders(
      //缓存
      CACHE_CONTROL -> "max-age=3600",
      CONTENT_DISPOSITION -> ("attachment; filename=" + name + "_complete.fasta"),
      "HttpResponse.entity.contentType" -> "application/x-download"
    )
  }


}
